using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Globalization;
using Instedd.Labs.Geo.Library.Kml;
using Instedd.Labs.Geo.Library.Kml.Styles;
using KmlPath = Instedd.Labs.Geo.Library.Kml.Path;

namespace Instedd.Labs.Geo.Library.Kml
{
    public class KmlWriter
    {
        public void WriteKml(XmlWriter writer, KmlFormat KmlFormat)
        {
            writer.WriteStartElement("kml", "http://earth.google.com/kml/2.2");

            writer.WriteStartElement("Document");

            KmlFormat.Styles.ForEach(delegate(Style s) { WriteStyle(writer, s); });
            KmlFormat.StyleMaps.ForEach(delegate(StyleMap s) { WriteStyle(writer, s); });
            KmlFormat.Folders.ForEach(delegate(Folder f) { WriteFolder(writer, f); });

            writer.WriteEndElement();
            writer.WriteEndElement();
        }

        private void WriteStyle(XmlWriter writer, StyleMap style)
        {
            writer.WriteStartElement("StyleMap");
            writer.WriteAttributeString("id", style.ID);

            if (!string.IsNullOrEmpty(style.NormalStyleUrl))
            {
                writer.WriteStartElement("Pair");
                writer.WriteElementString("key", "normal");
                writer.WriteElementString("styleUrl", StylesUtil.GenerateStyleUrl(style.NormalStyleUrl));
                writer.WriteEndElement();
            }

            if (!string.IsNullOrEmpty(style.HighlightStyleUrl))
            {
                writer.WriteStartElement("Pair");
                writer.WriteElementString("key", "highlight");
                writer.WriteElementString("styleUrl", StylesUtil.GenerateStyleUrl(style.HighlightStyleUrl));
                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }

        private void WriteStyle(XmlWriter writer, Style style)
        {
            writer.WriteStartElement("Style");
            writer.WriteAttributeString("id", style.ID);

            if (style.LineStyle != null)
            {
                WriteLineStyle(writer, style.LineStyle);
            }
            if (style.IconStyle != null)
            {
                WriteIconStyle(writer, style.IconStyle);
            }
            if (style.LabelStyle != null)
            {
                WriteLabelStyle(writer, style.LabelStyle);
            }

            writer.WriteEndElement();
        }

        private void WriteLineStyle(XmlWriter writer, LineStyle lineStyle)
        {
            writer.WriteStartElement("LineStyle");
            writer.WriteElementString("color", lineStyle.Color.ToString());
            writer.WriteElementString("width", lineStyle.Width.ToString(CultureInfo.InvariantCulture));
            writer.WriteEndElement();
        }

        private void WriteIconStyle(XmlWriter writer, IconStyle iconStyle)
        {
            writer.WriteStartElement("IconStyle");
            if (iconStyle.Color != null)
            {
                writer.WriteElementString("color", iconStyle.Color.ToString());
            }
            writer.WriteStartElement("Icon");
            writer.WriteElementString("href", iconStyle.IconUri.AbsoluteUri);
            writer.WriteEndElement();
            writer.WriteEndElement();
        }
        
        private void WriteLabelStyle(XmlWriter writer, LabelStyle labelStyle)
        {
            writer.WriteStartElement("LabelStyle");
            writer.WriteElementString("color", labelStyle.Color.ToString());
            writer.WriteEndElement();
        }

        private void WriteFolder(XmlWriter writer, Folder folder)
        {
            writer.WriteStartElement("Folder");
            writer.WriteElementString("name", folder.Name);

            folder.Placemarks.ForEach(delegate(Placemark placemark) { WritePlacemark(writer, placemark); });
            folder.Paths.ForEach(delegate(KmlPath path) { WritePath(writer, path); });

            writer.WriteEndElement();
        }

        private void WritePlacemark(XmlWriter writer, Placemark placemark)
        {
            writer.WriteStartElement("Placemark");

            writer.WriteElementString("name", placemark.Name);
            writer.WriteElementString("description", placemark.Description);

            if (placemark.LookAt != null)
            {
                WriteLookAt(writer, placemark.LookAt);
            }

            if (!string.IsNullOrEmpty(placemark.StyleUrl))
            {
                writer.WriteElementString("styleUrl", StylesUtil.GenerateStyleUrl(placemark.StyleUrl));
            }

            WriteTimestamp(writer, placemark.Timestamp);

            writer.WriteStartElement("Point");

            if (placemark.Location != null)
            {
                writer.WriteElementString("coordinates",
                    string.Format("{0},{1},{2}",
                        placemark.Location.Long.ToString(CultureInfo.InvariantCulture),
                        placemark.Location.Lat.ToString(CultureInfo.InvariantCulture),
                        placemark.Location.Alt.ToString(CultureInfo.InvariantCulture)
                        ));
            }

            writer.WriteEndElement();
            writer.WriteEndElement();
        }

        private void WriteTimestamp(XmlWriter writer, DateTime timestamp)
        {
            writer.WriteStartElement("TimeStamp");
            writer.WriteElementString("when", timestamp.ToString("yyyy-MM-ddTHH:mm:sszzzzzz", CultureInfo.InvariantCulture));
            writer.WriteEndElement();
        }

        private void WriteLookAt(XmlWriter writer, LookAt lookAt)
        {
            writer.WriteStartElement("LookAt");

            if (lookAt.Location != null)
            {
                writer.WriteElementString("longitude", lookAt.Location.Long.ToString(CultureInfo.InvariantCulture));
                writer.WriteElementString("latitude", lookAt.Location.Lat.ToString(CultureInfo.InvariantCulture));
                writer.WriteElementString("altitude", lookAt.Location.Alt.ToString(CultureInfo.InvariantCulture));
            }
            writer.WriteElementString("range", lookAt.Range.ToString(CultureInfo.InvariantCulture));
            writer.WriteElementString("tilt", lookAt.Tilt.ToString(CultureInfo.InvariantCulture));
            writer.WriteElementString("heading", lookAt.Heading.ToString(CultureInfo.InvariantCulture));

            switch (lookAt.AltitudeMode)
            {
                case AltitudeMode.Absolute: writer.WriteElementString("altitudeMode", "absolute"); break;
                case AltitudeMode.ClampToGround: writer.WriteElementString("altitudeMode", "clampToGround"); break;
                case AltitudeMode.RelativeToGround: writer.WriteElementString("altitudeMode", "relativeToGround"); break;
            }

            writer.WriteEndElement(); 
        }

        private void WritePath(XmlWriter writer, KmlPath path)
        {
            writer.WriteStartElement("Placemark");

            writer.WriteElementString("name", path.Name);

            if (path.Style != null)
            {
                writer.WriteElementString("styleUrl", StylesUtil.GenerateStyleUrl(path.Style.ID));
            }

            writer.WriteStartElement("LineString");

            StringBuilder pathCoordinates = new StringBuilder();
            path.Coordinates.ForEach(
                delegate(LatLong coordinate)
                {
                    pathCoordinates.Append(string.Format("{0},{1} ",
                        coordinate.Long.ToString(CultureInfo.InvariantCulture),
                        coordinate.Lat.ToString(CultureInfo.InvariantCulture)
                        ));
                }
                );

            writer.WriteElementString("coordinates", pathCoordinates.ToString());

            writer.WriteEndElement();
            writer.WriteEndElement();
        }

    }
}
